#include "coordinategrid.h"
#include <math.h>

CoordinateGrid::CoordinateGrid()
{
    _axisColor = QColor(0, 0, 0);
    _gridLinesColor = QColor(217, 217, 217);
    _renderMode = LINEAR;
}

void CoordinateGrid::setBoundingBox(double centerX, double centerY, double width, double heigth)
{
    _centerX = centerX;
    _centerY = centerY;
    _halfWidth = width/2.0;
    _halfHeight = heigth/2.0;
}

void CoordinateGrid::setStep(double dx, double dy)
{
    if(dx < 1.0) dx = 1.0;
    if(dy < 1.0) dy = 1.0;
    _dx = dx;
    _dy = dy;
}

void CoordinateGrid::draw(QGLWidget *textRenderer)
{
    glColor3d(_gridLinesColor.red()/255.0, _gridLinesColor.green()/255.0, _gridLinesColor.blue()/255.0);
    _drawLines();
    glColor3d(_axisColor.red()/255.0, _axisColor.green()/255.0, _axisColor.blue()/255.0);
    _drawAxis();

    _drawText(textRenderer);
}

void CoordinateGrid::switchToLogarithmicCoords()
{
    _renderMode = LOGARITHMIC;
}

void CoordinateGrid::switchToLinearCoords()
{
    _renderMode = LINEAR;
}

double CoordinateGrid::getNearestHumaReadableStep(double number)
{
    static const double dividers[] = { 1, 2, 5, 100, 1000, 10000 };
    static const int dividersCount = sizeof(dividers)/sizeof(dividers[0]);

    double step = dividers[0];
    for(int i=dividersCount-1; i>=0; i--)
    {
        if(number >= dividers[i])
        {
            double rest = fmod(number, dividers[i]);
            step = number - rest;
            break;
        }
    }

    return floor(step + 0.5);
}

void CoordinateGrid::_drawAxis()
{
    QRectF box = _getTranslatedAlignedBoundingBox();
    glLineWidth(3);

    double x = 0.0;
    double y = 0.0;

    glBegin(GL_LINES);
    glVertex2d(x, box.top());
    glVertex2d(x, box.bottom());
    glVertex2d(box.left(), y);
    glVertex2d(box.right(), y);

    glEnd();
    glLineWidth(1);
}



void CoordinateGrid::_drawLines()
{    
    QRectF box = _getTranslatedAlignedBoundingBox();

    glBegin(GL_LINES);
    int i=0;
    for(double x = box.left(); x < box.right(); i++)
    {
        x = box.left() + _dx * i;
        glVertex2d(x, box.top());
        glVertex2d(x, box.bottom());
    }

    int j=0;
    for(int y = box.top(); y <= box.bottom(); j++)
    {
        y = box.top() + _dy * j;
        glVertex2d(box.left(), y);
        glVertex2d(box.right(), y);
    }
    glEnd();
}

void CoordinateGrid::_drawText(QGLWidget *textRenderer)
{
    QRectF box = _getTranslatedAlignedBoundingBox();

    int i=0;
    double letterHeight = _dy/20.0;
    double textY = _getTextPositionY(letterHeight);
    for(double x = box.left(); x < box.right(); i++)
    {
        x = box.left() + _dx * i;
        if(x != 0)
            textRenderer->renderText(x, textY, 0,  _createCoordLabel(x));
    }

    int j=0;
    double letterWidth = _dx/30.0;
    for(int y = box.top(); y <= box.bottom(); j++)
    {
        y = box.top() + _dy * j;

        QString label = _createCoordLabel(y);
        double textX = _getTextPositionX(letterWidth * (label.length()));
        if(y != 0)
            textRenderer->renderText(textX, y, 0.0, label);
    }
}

double CoordinateGrid::_getTextPositionX(double textWidth)
{
    double textX = _getTextPosition(_centerX - _halfWidth, _centerX + _halfWidth);

    if(textX < 0)
        textX -= 2*textWidth;

    return textX;
}

double CoordinateGrid::_getTextPositionY(double textHeight)
{
    double textY = _getTextPosition(_centerY - _halfHeight, _centerY + _halfHeight);
    if(textY == 0.0)
        textY += textHeight;
    else if(textY < 0)
        textY -= 2*textHeight;
    else
        textY += textHeight;

    return textY;
}

double CoordinateGrid::_getTextPosition(double minCoord, double maxCoord)
{
    //if min-max line contains 0 - draw axis at zero
    if( (minCoord <= 0 && maxCoord >= 0) ||
        (minCoord >= 0 && maxCoord <= 0))
    {
        return 0.0;
    }

    //else draw axis at position nearest to zero
    double minAbsCoord = min(abs(minCoord), abs(maxCoord));
    if(minCoord <= 0)
        return -minAbsCoord;
    return minAbsCoord;
}


QString CoordinateGrid::_createCoordLabel(double coord)
{
    QString label= "0";
    if(_renderMode == LINEAR)
    {
        if(abs(coord) < 1000)
            label.sprintf(" %.0f", coord);
        else
            label.sprintf(" %.2e", coord);
    }
    if(_renderMode == LOGARITHMIC)
    {
        if(coord > 0)
            label.sprintf(" 10^%d", (int)coord-1);
        else if(coord < 0)
            label.sprintf(" -10^%d", -(int)coord-1);
    }
    return label;
}

QRectF CoordinateGrid::_getTranslatedAlignedBoundingBox()
{
    double leftX = _centerX - _halfWidth - fmod(_centerX - _halfWidth, _dx) - _dx;
    double topY = _centerY - _halfHeight - fmod(_centerY - _halfHeight, _dy) - _dy;
    double width = 2*_halfWidth + 2*_dx;
    double height = 2*_halfHeight + 2*_dy;

    return QRectF(leftX, topY, width, height);
}



void CoordinateGrid::setAxisColor(QColor color)
{
    _axisColor = color;
}

void CoordinateGrid::setGridLinesColor(QColor color)
{
    _gridLinesColor = color;
}

double CoordinateGrid::getXCoordFromPoint(double x)
{
    if(_renderMode == LOGARITHMIC)
    {
        double res = x;
        double sign = 1.0;
        if(x < 0)
        {
            sign = -1.0;
            x = -x;
        }

        if(x < 1.0)
            x = log(x)/log(2.0);
        res = pow(10.0, x);
        return sign * res;
    }
    return x;
}
